﻿using DeHeng_Mes.Constant;
using DeHeng_Mes.Dao;
using DeHeng_Mes.Dao.PlcDao;
using DeHeng_Mes.Mapper;
using DeHeng_Mes.Model;
using DeHeng_Mes.Model.Boss;
using DeHeng_Mes.Model.ConfigModel;
using DeHeng_Mes.Model.Table;
using DeHeng_Mes.Utils;
using S7.Net;
using SqlSugar;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using MappingTable = DeHeng_Mes.Utils.MappingTable;

namespace DeHeng_Mes.Thread
{
    public class LinePlcToMes
    {
        #region 全局成员
        OrderDao orderDao = new OrderDao();
        PlcDao plcDao = new PlcDao();
        DeviceDao deviceDao = new DeviceDao();
        ProduceDao produceDao = new ProduceDao();
        CodeTraceDao codeTraceDao = new CodeTraceDao();
        List<PlcOrderConfigModel> readPlcList = PlcOrderConfigModelList.ReadPlcList;
        Dictionary<string, Dictionary<string, bool>> saveStatus = StationSaveStatusDic.SaveStatus;
        CodeDataCollect dataCollect = new CodeDataCollect();
        #endregion

        /// <summary>
        /// 处理订单开始与结束的数据读取写入
        /// </summary>
        public async Task<bool> OrderStatus(Dictionary<object, Type> types, PlcOrderConfigModel orderModel)
        {
            bool isOk = false;

            try
            {
                // 获取订单状态，通过订单号查找数据库中的订单信息
                var orderStatuses = orderDao.GetOrder(orderModel.orderPlan.OrderId);

                // 判断订单是否存在或者是否在开始状态
                if (orderStatuses != null && (string.IsNullOrEmpty(orderStatuses.Status) || !orderStatuses.Status.Equals("1")))
                {
                    // 如果工单不在开始状态，更新订单状态
                    StartNewOrder(types, orderModel);
                }
                else
                {
                    // 订单存在或者状态为“1”，表示订单未完成，继续检查PLC状态
                    if (plcDao.GetBoolFieldValue(types, SystemConstant.StationStatus, "EndWorkStatus" + orderModel.OrderNumInPlc))
                    {
                        // 如果PLC发出了“结束工作”信号，处理订单完成
                        CompleteOrder(types, orderModel);
                        // 发出工单处理完成信号
                        orderModel.plc.WriteBit(DataType.DataBlock, SystemConstant.Order, orderModel.OrderNumInPlc.Equals("1") ? 770 : 770 + (772 * (int.Parse(orderModel.OrderNumInPlc) - 1)), 3, true);

                        // 返回处理完成状态
                        return isOk = true;
                    }
                    else
                    {
                        // 如果订单未完成，且PLC没有结束信号，检查是否有开工信号
                        if (plcDao.GetBoolFieldValue(types, SystemConstant.StationStatus, "StartWorkStatus" + orderModel.OrderNumInPlc))
                        {
                            BossMessager bossMessager = null;

                            // 判断是否写入了首次开工的时间
                            if (orderStatuses != null && !orderStatuses.StartWorkStatus.Equals(SystemConstant.Ok))
                                if (string.IsNullOrEmpty(orderStatuses.StartTime.ToString()) || DateTime.MinValue == orderStatuses.StartTime)
                                    orderDao.UpdateStartTime(orderStatuses.Id, DateTime.Now);
                                else
                                    bossMessager = await PutMes.LineMesToMes(new StartOrder()
                                    {
                                        OrderId = orderStatuses.OrderId,
                                        StartTime = orderStatuses.StartTime.ToString()
                                    }, SystemConstant.StartTimeUrl, $"工单开始时间推送，工单号为：{orderModel.orderPlan.OrderId}，开始时间为{orderStatuses.StartTime}，任务推送");

                            // 判断推送状态，推送成功那么更新推送状态
                            if (bossMessager != null && bossMessager.code == 200)
                            {
                                orderDao.UpdateStartTimeStatus(orderStatuses.Id);
                                orderModel.orderPlan.StartTime = orderStatuses.StartTime.ToString();
                            }
                            // 采集数据
                            StartDataCollection(types, orderModel);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                // 处理异常，记录错误信息
                Tools.ProduceLogTextError("工单信息处理异常，原因是：" + ex.Message);
            }

            return isOk;  // 返回订单是否处理完成的状态
        }

        /// <summary>
        /// 更新订单开始状态
        /// </summary>
        /// <param name="types"></param>
        /// <param name="orderModel"></param>
        private void StartNewOrder(Dictionary<object, Type> types, PlcOrderConfigModel orderModel)
        {
            // 创建新的订单状态对象并初始化
            var orderStatus = new OrderStatus
            {
                OrderId = orderModel.orderPlan.OrderId,
                PlcName = orderModel.PlcName,  // 订单对应的PLC名称
                Status = "1",  // 设置为“开始生产”状态
                StartPower = plcDao.GetStringFieldValue(types, SystemConstant.Power, "PowerNow"),  // 获取当前功率
                StartGas = plcDao.GetStringFieldValue(types, SystemConstant.Power, "GasNow")  // 获取当前气体消耗
            };

            // 更新订单开始状态到数据库
            orderDao.UpdateStartOrder(orderStatus);
            Tools.ProduceLogText($"{orderStatus.OrderId}开始生产...");  // 记录订单开始生产的日志
        }

        /// <summary>
        /// 更新订单结束状态
        /// </summary>
        /// <param name="types"></param>
        /// <param name="orderModel"></param>
        /// <returns></returns>
        private void CompleteOrder(Dictionary<object, Type> types, PlcOrderConfigModel orderModel)
        {
            // 完成订单时，计算产量数据
            var (produceOk, produceNg) = CalculateProductionData(types, orderModel);

            // 创建订单完成状态对象并初始化
            var orderStatus = new OrderStatus
            {
                OrderId = orderModel.orderPlan.OrderId,  // 订单ID
                EndTime = DateTime.Now,  // 设置订单结束时间
                EndPower = plcDao.GetStringFieldValue(types, SystemConstant.Power, "PowerNow"),  // 获取结束时功率
                EndGas = plcDao.GetStringFieldValue(types, SystemConstant.Power, "GasNow"),  // 获取结束时气体消耗
                ProductionNg = produceNg,  // 设置NG产量
                ProductionOk = produceOk,  // 设置OK产量
                Status = "2"  // 设置订单状态为“完成”
            };

            // 更新订单完成状态到数据库
            orderDao.UpdateOrder(orderStatus);

            // 删除订单相关的Ng二维码信息
            codeTraceDao.DeleteAllByOrder(orderModel.orderPlan.OrderId);

            // 删除推送成功的订单相关的小时产量信息
            produceDao.DeleteAllProduceByOrderId(orderModel.orderPlan.OrderId);

            // 删除故障表中数据
            deviceDao.DeleteAllByOrderId(orderModel.orderPlan.OrderId);

            // 删除安灯推送缓存表
            deviceDao.DeleteAndonById(orderModel.orderPlan.OrderId);

            // 删除总成码生成信息的缓存
            codeTraceDao.DeleteByOrderId(orderModel.orderPlan.OrderId);

            // 记录订单完成的日志
            Tools.ProduceLogText($"工单{orderModel.orderPlan.OrderId}已经完成，订单相关数据保存成功，结束时间为{orderStatus.EndTime}，结束耗电量为：{orderStatus.EndPower}，" +
                $"结束耗气量为{orderStatus.EndGas}，Ng产量为{orderStatus.ProductionNg}，Ok产量为{orderStatus.ProductionOk}。");
        }

        /// <summary>
        /// 计算Ok产量与Ng产量
        /// </summary>
        /// <param name="types"></param>
        /// <param name="orderModel"></param>
        /// <returns></returns>
        private (string produceOk, string produceNg) CalculateProductionData(Dictionary<object, Type> types, PlcOrderConfigModel orderModel)
        {
            // 初始化产量相关的变量
            string produceOk = "0";
            string produceNg = "0";
            string produceTotal = "0";

            try
            {
                // 更新当前订单的最后一小时产量数据
                ProductionNow(types, orderModel);

                // 更新最后一小时工序产量数据，并且报工工厂Mes
                OperationProductionNow(types, orderModel);

                // 查询当前订单所有产量信息
                var productionHours = produceDao.QueryProduceTotal(orderModel.orderPlan.OrderId);

                // 遍历每小时的产量数据，累加产量和NG产量
                foreach (var productionHour in productionHours)
                {
                    produceTotal = (int.Parse(produceTotal) + int.Parse(productionHour.Produce)).ToString();
                    produceNg = (int.Parse(produceNg) + int.Parse(productionHour.ProduceNg)).ToString();
                }

                // 计算OK产量
                produceOk = (int.Parse(produceTotal) - int.Parse(produceNg)).ToString();

                // 返回计算后的OK产量和NG产量
                return (produceOk, produceNg);
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError($"工单{orderModel.orderPlan.OrderId}结束时计算Ok产量与Ng产量失败，原因是： {ex.Message}");
            }
            return (produceOk, produceNg);
        }

        /// <summary>
        /// 保存最后一小时的产量信息
        /// </summary>
        /// <param name="types"></param>
        /// <param name="orderModel"></param>
        private async void OperationProductionNow(Dictionary<object, Type> types, PlcOrderConfigModel orderModel)
        {
            try
            {

                // 初始化Mes数据List
                List<BossProductionHour> bossProductionHours = new List<BossProductionHour>();

                List<BossDeviceFault> bossDeviceFaults = new List<BossDeviceFault>();

                // 获取当前时间
                var currentTime = DateTime.Now;

                // 计算当前小时的时间段
                var timePeriod = $"{currentTime.Hour}-{(currentTime.Hour == 23 ? 0 : currentTime.Hour + 1)}";

                // 计算开始时间与结束时间
                string[] times = timePeriod.Split('-');

                //转换成Mes可以接收的小时类型
                timePeriod = Tools.ConvertToTimeRange(timePeriod);

                // 获取当前小时每个工序产量值
                foreach (string operations in orderModel.orderPlan.ProcessCodes)
                {
                    // 使用对照表替换工序
                    (string operation, bool isPut) = MappingTable.GetOperation(operations, orderModel.orderPlan.MaterialCode);

                    // 获取该工序的当前小时总产量
                    string operationValue = plcDao.GetStringFieldValue(types, SystemConstant.ProduceOperation, operation + "_Now");

                    // 获取该工序的当前小时Ng总产量
                    string operationNgValue = plcDao.GetStringFieldValue(types, SystemConstant.ProduceOperation, operation + "Ng_Now");

                    // 获取时间段内的报警数据
                    List<DeviceFault> deviceFaults = Tools.ErrorMessageInTime(int.Parse(times[0]), int.Parse(times[1]), operation);

                    // 计算时间段内的停机时长
                    string timeLong = Tools.DateTimeCalculate(deviceFaults, int.Parse(times[0]), int.Parse(times[1]));

                    // 构建报警数据
                    foreach (var deviceFault in deviceFaults)
                    {
                        var bossDeviceFault = new BossDeviceFault()
                        {
                            DeviceId = deviceFault.DeviceId,
                            FaultMessage = deviceFault.FaultMessage,
                            FaultTime = decimal.Parse(string.IsNullOrEmpty(deviceFault.FaultTime) ? "0" : deviceFault.FaultTime),
                            FaultType = deviceFault.FaultType,
                            StopStartTime = deviceFault.StopStartTime.ToString(),
                            StopEndTime = deviceFault.StopEndTime.ToString()
                        };
                        bossDeviceFaults.Add(bossDeviceFault);
                    }

                    // 构建数据，并且推送数据
                    var bossProductionHour = new BossProductionHour()
                    {
                        Operation = operations,
                        OrderId = orderModel.orderPlan.OrderId,
                        NowTime = timePeriod,
                        Produce = decimal.Parse(string.IsNullOrEmpty(operationValue) ? "0" : operationValue),
                        ProduceNg = decimal.Parse(string.IsNullOrEmpty(operationNgValue) ? "0" : operationNgValue),
                        TimeLong = decimal.Parse(timeLong),
                        DeviceFaults = bossDeviceFaults
                    };
                    bossProductionHours.Add(bossProductionHour);
                }
                // 调用接口推送数据
                BossMessager bossMessager = await PutMes.LineMesToMes(bossProductionHours, SystemConstant.ProduceHourUrl, $"工序产量报工任务，工单号为：{orderModel.orderPlan.OrderId}，推送");

                // 判断推送状态进一步操作
                if (bossMessager != null && bossMessager.code != 200)
                {
                    foreach (var bossProductionHour in bossProductionHours)
                    {
                        // 推送失败，暂存数据库
                        produceDao.InsertOperation(orderModel.orderPlan.OrderId, bossProductionHour.Operation, bossProductionHour.Produce.ToString(), bossProductionHour.ProduceNg.ToString()
                            , timePeriod, bossProductionHour.TimeLong.ToString(), SystemConstant.illegal);
                    }
                }
            }
            catch (Exception e)
            {
                Tools.ProduceLogTextError($"保存或推送{orderModel.orderPlan.OrderId}工单最后一小时数据异常，原因是{e.Message}");
            }
        }

        /// <summary>
        /// 处理Plc数据采集与部分工厂Mes数据推送
        /// </summary>
        /// <param name="types"></param>
        /// <param name="orderModel"></param>
        private void StartDataCollection(Dictionary<object, Type> types, PlcOrderConfigModel orderModel)
        {
            // 扫描PLC二维码获取信号
            // Task.Run(() => ProcessGetCode(types, orderModel));
            // 扫描工序完成状态
            Task.Run(() => ProcessOkStatus(types, orderModel));
            // 扫描设备故障信息
            //Task.Run(() => DeviceFaultRead(types, orderModel));
            // 扫描每小时工序产量
            Task.Run(() => ProductionOperationRead(types, orderModel));
            // 扫描每小时工单总产量
            Task.Run(() => ProductionOrderRead(types, orderModel));
            // 扫描各个工位、工艺报警次数
            //Task.Run(() => ErrorCount(types, orderModel));
            // 扫描易损件信息
            //Task.Run(() => VulnerablePart(types, orderModel));
            // 执行信号复位
            //Task.Run(() => SignalReset(types, orderModel));
        }

        /// <summary>
        /// 更新工单总产量
        /// </summary>
        /// <param name="types"></param>
        /// <param name="orderModel"></param>
        private void ProductionOrderRead(Dictionary<object, Type> types, PlcOrderConfigModel orderModel)
        {
            try
            {
                // 获取当前时间并计算上一个小时的时间段
                DateTime currentTime = DateTime.Now;
                int currentHour = currentTime.Hour;
                int previousHour = currentHour == 0 ? 23 : currentHour - 1;
                string timePeriod = $"{previousHour}-{currentHour}";

                // 检查数据库是否已保存上一小时的总产量数据
                bool isLastHourSaved = produceDao.IsSaveLastTime(timePeriod, orderModel.orderPlan.OrderId);
                if (isLastHourSaved) return;

                // 从 PLC 获取上小时的生产总量和 NG 数量
                Dictionary<string, object> productionData = plcDao.GetFieldValues(types, SystemConstant.ProduceOrder);
                string produce = productionData.ContainsKey("HourFirstProduce" + orderModel.OrderNumInPlc)
                    ? productionData["HourFirstProduce" + orderModel.OrderNumInPlc].ToString()
                    : "数据提取失败";
                string produceNg = productionData.ContainsKey("ProduceFirst_Ng" + orderModel.OrderNumInPlc)
                    ? productionData["ProduceFirst_Ng" + orderModel.OrderNumInPlc].ToString()
                    : "数据提取失败";
                // 获取时间段内的报警数据和停机时长
                List<DeviceFault> deviceFaults = Tools.ErrorMessageInTime(previousHour, currentHour, orderModel.PlcName);
                string downtime = Tools.DateTimeCalculate(deviceFaults, previousHour, currentHour);

                // 保存产量数据到数据库
                int resultCount = produceDao.InsertProduce(orderModel.orderPlan.OrderId, timePeriod, produce, produceNg, downtime);
                Tools.ProduceLogText($"{orderModel.orderPlan.OrderId}成功保存{timePeriod}点的产量数据，产量为：{produce}, 其中 NG 产量为 {produceNg}");
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError($"更新工单关联产量失败，原因是：{ex.Message}");
            }
        }


        /// <summary>
        /// 更新当前小时产量
        /// </summary>
        /// <param name="types"></param>
        /// <param name="plcModel"></param>
        private void ProductionNow(Dictionary<object, Type> types, PlcOrderConfigModel orderModel)
        {
            try
            {
                //获取当前小时，判断上一个小时是几点
                // 获取当前时间
                DateTime currentTime = DateTime.Now;
                // 获取当前小时
                int currentHour = currentTime.Hour;
                //获取下一小时
                int previousHour = currentHour == 0 ? 23 : currentHour + 1;
                string time = $"{currentHour}-{previousHour}";
                //查找数据库是否保存当前对应的产量
                if (produceDao.IsSaveLastTime(time, orderModel.orderPlan.OrderId)) return;

                //未保存，保存数据
                //获取当前小时产量字段
                string nowProduce = plcDao.GetStringFieldValue(types, SystemConstant.ProduceOrder, "Produce" + orderModel.OrderNumInPlc);
                string nowProduce_Ng = plcDao.GetStringFieldValue(types, SystemConstant.ProduceOrder, "Produce_Ng" + orderModel.OrderNumInPlc);
                //获取时间段内的报警数据
                List<DeviceFault> deviceFaults = Tools.ErrorMessageInTime(currentHour, previousHour, orderModel.PlcName);
                //获取时间段内的停机时长
                string timeLong = Tools.DateTimeCalculate(deviceFaults, currentHour, previousHour);
                //保存数据库
                int count = produceDao.InsertProduce(orderModel.orderPlan.OrderId, $"{currentHour}-{previousHour}", nowProduce, nowProduce_Ng, timeLong);
                Tools.ProduceLogText($"{orderModel.PlcName}成功保存{currentHour}-{previousHour}点钟最后一小时的产量数据，产量为：{nowProduce},订单号为：{orderModel.orderPlan.OrderId}");
            }
            catch (Exception e)
            {
                Tools.ProduceLogTextError($"{orderModel.orderPlan.OrderId}最后一小时产量数据异常，原因是：" + e.Message);
            }
        }

        /// <summary>
        /// 处理易损件数据
        /// </summary>
        /// <param name="types"></param>
        /// <param name="plcModel"></param>
        private void VulnerablePart(Dictionary<object, Type> types, PlcOrderConfigModel plcModel)
        {

        }

        /// <summary>
        /// 处理信号复位
        /// </summary>
        /// <param name="types"></param>
        /// <exception cref="NotImplementedException"></exception>
        private bool SignalReset(Dictionary<object, Type> types, PlcOrderConfigModel plcModel)
        {
            bool end = false;
            Dictionary<string, object> errorDic = plcDao.GetFieldValues(types, SystemConstant.DeviceErrorStatus);
            //处理报警信息读取的状态位
            foreach (var item in errorDic)
            {
                //获取Plc反馈状态位
                string isOk = item.Value.ToString();
                //获取偏移量
                (int, int) values = MappingTable.GetOff(item.Key);
                //获取已读状态位
                bool isRead = (bool)plcModel.plc.Read(DataType.DataBlock, SystemConstant.Write, values.Item1, VarType.Bit, 1, byte.Parse(values.Item2.ToString()));
                if (isOk.Equals("0") && isRead)
                {
                    plcModel.plc.WriteBit(DataType.DataBlock, SystemConstant.Write, values.Item1, values.Item2, false);
                }
            }

            return end;
        }


        /// <summary>
        /// 实时保存当前工单的报错次数
        /// </summary>
        /// <param name="types"></param>
        /// <param name="plcModel"></param>
        private void ErrorCount(Dictionary<object, Type> types, PlcOrderConfigModel orderModel)
        {
            try
            {
                //获取当前工单，查询数据库判断是否已经存在当前工单数据
                int count = deviceDao.SelectOrderIsSave(orderModel.orderPlan.OrderId);
                Dictionary<string, object> warnDic = plcDao.GetFieldValues(types, SystemConstant.StationError);
                foreach (var item in warnDic)
                {
                    if (item.Key.Contains("CurrentShift"))
                    {
                        //班次信息，跳过
                        continue;
                    }
                    else if (count > 0)
                    {
                        //执行更新操作
                        deviceDao.UpdateWarnCount(orderModel.orderPlan.OrderId, item.Key, item.Value.ToString());
                        Tools.ProduceLogText($"更新了一条{orderModel.orderPlan.OrderId}中的报错次数,类型为{item.Key}，次数为{item.Value}");
                    }
                    else
                    {
                        //执行插入操作
                        deviceDao.InsertWarnCount(orderModel.orderPlan.OrderId, item.Key, item.Value.ToString());
                    }
                }
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError($"初始化或更新报错次数失败，来自于{orderModel.orderPlan.OrderId}，原因是{ex.Message}");
            }
        }

        /// <summary>
        /// 小时工序产量信息采集
        /// </summary>
        /// <param name="types">包含生产数据字段类型的字典</param>
        /// <param name="orderModel">当前工单的配置信息模型</param>
        private async void ProductionOperationRead(Dictionary<object, Type> types, PlcOrderConfigModel orderModel)
        {
            try
            {
                if (string.IsNullOrEmpty(orderModel.orderPlan.StartTime) || CanPush(orderModel.orderPlan.StartTime))
                    return;

                BossMessager bossMessager = null;

                // 初始化Mes数据List
                List<BossProductionHour> bossProductionHours = new List<BossProductionHour>();

                List<BossDeviceFault> bossDeviceFaults = new List<BossDeviceFault>();

                // 获取当前时间
                var currentTime = DateTime.Now;

                // 计算上一小时时间间隔
                var timePeriod = $"{(currentTime.Hour == 0 ? 23 : currentTime.Hour - 1)}-{currentTime.Hour}";

                // 计算开始时间与结束时间
                string[] times = timePeriod.Split('-');

                //转换成Mes可以接收的小时类型
                timePeriod = Tools.ConvertToTimeRange(timePeriod);

                // 获取上小时每个工序产量值
                foreach (var operations in orderModel.orderPlan.ProcessCodes)
                {
                    // 获取线体实际工序
                    (string operation, bool isPut) = MappingTable.GetOperation(operations, orderModel.orderPlan.MaterialCode);

                    // 获取该工序的上小时总产量
                    string operationValue = plcDao.GetStringFieldValue(types, SystemConstant.ProduceOperation, operation);

                    // 获取该工序的上小时Ng总产量
                    string operationNgValue = plcDao.GetStringFieldValue(types, SystemConstant.ProduceOperation, operation + "Ng");

                    // 获取时间段内的报警数据
                    List<DeviceFault> deviceFaults = Tools.ErrorMessageInTime(int.Parse(times[0]), int.Parse(times[1]), operation);

                    // 计算时间段内的停机时长
                    string timeLong = Tools.DateTimeCalculate(deviceFaults, int.Parse(times[0]), int.Parse(times[1]));

                    // 构建报警数据
                    foreach (var deviceFault in deviceFaults)
                    {
                        var bossDeviceFault = new BossDeviceFault()
                        {
                            DeviceId = deviceFault.DeviceId,
                            FaultMessage = deviceFault.FaultMessage,
                            FaultTime = decimal.Parse(deviceFault.FaultTime),
                            FaultType = deviceFault.FaultType,
                            StopStartTime = deviceFault.StopStartTime.ToString(),
                            StopEndTime = deviceFault.StopEndTime.ToString()
                        };
                        bossDeviceFaults.Add(bossDeviceFault);
                    }

                    // 构建数据，并且推送数据
                    var bossProductionHour = new BossProductionHour()
                    {
                        Operation = operations,
                        OrderId = orderModel.orderPlan.OrderId,
                        NowTime = timePeriod,
                        Produce = decimal.Parse(operationValue.Equals("") ? "0" : operationValue),
                        ProduceNg = decimal.Parse(operationNgValue.Equals("") ? "0" : operationNgValue),
                        TimeLong = decimal.Parse(timeLong.Equals("") ? "0" : timeLong),
                        DeviceFaults = bossDeviceFaults
                    };

                    bossProductionHours.Add(bossProductionHour);

                }

                if (!produceDao.ProduceIsSave(orderModel.orderPlan.OrderId, timePeriod))
                    // 调用接口推送数据
                    bossMessager = await PutMes.LineMesToMes(bossProductionHours, SystemConstant.ProduceHourUrl, $"工序产量报工任务，订单号为：{orderModel.orderPlan.OrderId}，推送");

                if (bossMessager != null && bossMessager.code == 200)
                {
                    foreach (var bossProductionHour in bossProductionHours)
                    {
                        // 推送成功，暂存数据
                        produceDao.InsertOperation(orderModel.orderPlan.OrderId, bossProductionHour.Operation, bossProductionHour.Produce.ToString(), bossProductionHour.ProduceNg.ToString()
                            , timePeriod, bossProductionHour.TimeLong.ToString(), SystemConstant.Ok);
                    }
                }
            }
            catch (Exception e)
            {
                Tools.ProduceLogTextError($"保存或推送{orderModel.orderPlan.OrderId}订单最后一小时数据异常，原因是{e.Message}");
            }
        }

        /// <summary>
        /// 处理设备报警相关
        /// </summary>
        /// <param name="types"></param>
        /// <param name="orderModel"></param>
        private async void DeviceFaultRead(Dictionary<object, Type> types, PlcOrderConfigModel orderModel)
        {
            try
            {
                // 从 PLC 获取所有设备报警状态值
                Dictionary<string, object> errorStatus = plcDao.GetFieldValues(types, SystemConstant.DeviceErrorStatus);

                foreach (var item in errorStatus)
                {
                    // 判断设备报警状态
                    bool isFault = item.Value.ToString() == SystemConstant.DeviceNOk;
                    bool isRecovery = item.Value.ToString() == SystemConstant.DeviceOk;

                    // 如果没有报警，不处理
                    if (!isFault || !isRecovery) continue;

                    // 获取并解析报警信息
                    string errorMessage = plcDao.GetStringFieldValue(types, SystemConstant.DeviceErrorMessage, item.Key.Replace("Status", "Message"));
                    string[] errorMessages = errorMessage.Split(new string[] { "_" }, StringSplitOptions.None);

                    // 确认报警信息格式是否符合预期
                    if (errorMessages.Length < 5 || errorMessages[errorMessages.Length - 1].Length >= 15)
                    {
                        Tools.ProduceLogTextError("Plc报警信息格式错误，无法解析，请相关人员检查");
                        continue;
                    }

                    // 解析故障信息
                    string faultType = errorMessages[0];
                    string operation = errorMessages[1];
                    string deviceId = errorMessages[2];
                    string faultMessage = errorMessages[3];
                    DateTime startTime = DateTime.ParseExact(errorMessages[4], "yyyyMMddHHmmss", System.Globalization.CultureInfo.InvariantCulture);
                    DateTime? endTime = isRecovery
                        ? DateTime.ParseExact(errorMessages[5], "yyyyMMddHHmmss", System.Globalization.CultureInfo.InvariantCulture)
                        : (DateTime?)null;

                    // 检查是否已保存该报警信息
                    bool isSaved = deviceDao.SelectMessage(orderModel.orderPlan.OrderId, deviceId, startTime, orderModel.PlcName);

                    // 处理报警信息
                    if (isFault && !isSaved)
                    {
                        // 保存新的故障信息
                        SaveDeviceFault(orderModel.orderPlan.OrderId, faultType, operation, deviceId, faultMessage, startTime, orderModel.PlcName);

                        // 实例化安灯信息
                        BossAndon bossAndon = new BossAndon();

                        // 检查新发生故障是否需要推送至工厂Mes
                        if (MappingTable.DeviceIdInAndon(deviceId, faultMessage) && !deviceDao.QueryByAll(orderModel.orderPlan.OrderId, operation, deviceId, faultMessage, faultType, errorMessages[4]))
                        {
                            BossMessager bossMessager = await PutMes.LineMesToMes(bossAndon = new BossAndon()
                            {
                                DeviceId = deviceId,
                                FaultMessage = faultMessage,
                                FaultType = faultType,
                                OrderId = orderModel.orderPlan.OrderId,
                                ProcessId = operation
                            }, SystemConstant.AndonUrl, $"安灯故障推送，工单号为：{orderModel.orderPlan.OrderId}，工序号为：{operation}，" +
                            $"设备号为：{deviceId}，故障类型为：{faultType}，故障消息为：{faultMessage}，故障开始时间为：{startTime}，任务推送");

                            if (bossMessager != null)
                                // 暂存数据库
                                deviceDao.InsertToFactory(bossAndon, bossMessager.code);
                        }
                    }
                    else if (isRecovery)
                    {
                        // 更新故障恢复信息，或插入新的故障信息
                        UpdateOrInsertRecovery(orderModel.orderPlan.OrderId, faultType, operation, deviceId, faultMessage, startTime, endTime.Value, orderModel.PlcName);

                        // 向 PLC 写入已读状态
                        (int dbNumber, int bitAddress) = MappingTable.GetOff(item.Key);
                        orderModel.plc.WriteBit(DataType.DataBlock, SystemConstant.Write, dbNumber, bitAddress, true);
                    }
                }
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError("停线故障信息记录报错，原因是：" + ex.Message);
            }
        }

        /// <summary>
        /// 保存故障信息
        /// </summary>
        /// <param name="faultType"></param>
        /// <param name="operation"></param>
        /// <param name="deviceId"></param>
        /// <param name="faultMessage"></param>
        /// <param name="startTime"></param>
        /// <param name="plcName"></param>
        private void SaveDeviceFault(string orderId, string faultType, string operation, string deviceId, string faultMessage, DateTime startTime, string plcName)
        {
            DeviceFault deviceFault = new DeviceFault
            {
                OrderId = orderId,
                PlcName = plcName,
                FaultType = faultType,
                Operation = operation,
                DeviceId = deviceId,
                FaultMessage = faultMessage,
                StopStartTime = startTime
            };

            int result = deviceDao.InsertError(deviceFault);
            Tools.ProduceLogText($"成功记录{result}条停线故障，故障原因是{faultMessage}");
        }

        /// <summary>
        /// 更新或插入恢复故障信息
        /// </summary>
        /// <param name="faultType"></param>
        /// <param name="operation"></param>
        /// <param name="deviceId"></param>
        /// <param name="faultMessage"></param>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <param name="plcName"></param>
        private void UpdateOrInsertRecovery(string orderId, string faultType, string operation, string deviceId, string faultMessage, DateTime startTime, DateTime endTime, string plcName)
        {
            DeviceFault deviceFault = new DeviceFault
            {
                OrderId = orderId,
                PlcName = plcName,
                FaultType = faultType,
                Operation = operation,
                DeviceId = deviceId,
                FaultMessage = faultMessage,
                StopStartTime = startTime,
                StopEndTime = endTime,
                FaultTime = Math.Floor((endTime - startTime).TotalMinutes).ToString()
            };

            //更新信息
            bool isUpdated = deviceDao.UpdateEndTimeError(deviceFault);

            if (isUpdated)
            {
                Tools.ProduceLogText($"成功更新1条故障恢复记录，恢复时间是{endTime}");
            }
            else
            {
                int result = deviceDao.InsertError(deviceFault);
                Tools.ProduceLogText($"成功记录{result}条停线故障，故障原因是{faultMessage}");
            }
        }

        /// <summary>
        /// 处理索要二维码信号，生成并下发新的二维码
        /// </summary>
        /// <param name="plcModel">PLC 配置模型</param>
        private void ProcessGetCode(Dictionary<object, Type> types, PlcOrderConfigModel orderModel)
        {
            try
            {
                // 获取总成码信号
                bool isGet = plcDao.GetBoolFieldValue(types, SystemConstant.StationStatus, "GetCode6");

                // 获取视觉码
                bool isGetVisionCode = plcDao.GetBoolFieldValue(types, SystemConstant.StationStatus, "GetCode7");

                // 处理总成码
                if (isGet && !orderModel.GetCodeStatus)
                {
                    orderModel.GetCodeStatus = true; // 记录状态

                    string traceCode = GetTraceCode(types, orderModel); // 生成实际总成码
                    orderModel.plc.Write(DataType.DataBlock, SystemConstant.GetCode, 1280, Tools.ConvertToPlcString(traceCode, 32));

                    Tools.ProduceLogText($"{orderModel.orderPlan.OrderId}生成总成码成功,总成码为{traceCode}");
                }
                else
                {
                    orderModel.GetCodeStatus = isGet;
                }

                // 下发视觉码
                if (isGetVisionCode && !orderModel.GetVisionCodeStatus)
                {
                    // 根据时间戳获取24位随机的纯数字码
                    string visionCode = VirtualCodeGenerator.GenerateRandomCode();

                    // 下发给Plc
                    orderModel.plc.Write(DataType.DataBlock, SystemConstant.GetCode, 1792, Tools.ConvertToPlcString(visionCode, visionCode.Length));

                    // 记录下发状态
                    orderModel.GetVisionCodeStatus = true;

                    Tools.ProduceLogText($"{orderModel.PlcName}成功下发一条视觉码，视觉码为{visionCode}");
                }
                else
                {
                    // 无信号，重置状态
                    orderModel.GetVisionCodeStatus = isGetVisionCode;
                }

                // 处理其余附属码
                var codeOffsets = new Dictionary<int, int>
                    {
                        {1, 0},
                        {2, 256},
                        {3, 512},
                        {4, 768},
                        {5, 1024}
                    };

                foreach (var keyValuePair in codeOffsets)
                {
                    int i = keyValuePair.Key;
                    int offset = keyValuePair.Value;

                    if (plcDao.GetBoolFieldValue(types, SystemConstant.StationStatus, $"GetCode{i}"))
                    {
                        string uniqueCode = VirtualCodeGenerator.GenerateVirtualCode();
                        orderModel.plc.Write(DataType.DataBlock, SystemConstant.GetCode, offset, Tools.ConvertToPlcString(uniqueCode, 32));
                    }
                }
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError("处理索要二维码信号，生成并下发新的二维码失败，原因是：" + ex.Message);
            }
        }

        /// <summary>
        /// 生成总成码，返回Plc
        /// </summary>
        /// <returns></returns>
        private string GetTraceCode(Dictionary<object, Type> types, PlcOrderConfigModel orderModel)
        {
            // 初始化总成码结构
            string part1 = "4326";
            string part2 = orderModel.orderPlan.PartId;
            string part4 = "";
            string part5 = "";

            try
            {

                // 根据年份计算出Part4第一个字母
                string part4_1 = MappingTable.GetYear(DateTime.Now.Year);

                // 根据月份计算出Part4第二个字母
                string part4_2 = MappingTable.GetMonth(DateTime.Now.Month);

                // 根据日期计算出Part4第三个字母
                string part4_3 = MappingTable.GetDay(DateTime.Now.Day);

                // 生成Part4(序列号)
                part4 = part4_1 + part4_2 + part4_3;

                // 根据班次计算出Part5第一个字母
                string currentShift = plcDao.GetStringFieldValue(types, SystemConstant.StationError, "CurrentShift");  //获取班次信息

                string part5_1 = currentShift.Equals("1") ? "A" : "B";

                string part5_2 = "F";

                // 初始化三位序列号
                string part5_3 = "001";

                // 根据批次生成三位序列号
                List<CodeCount> codeCounts = codeTraceDao.GetCount(part4, part5_1, "53011002450");

                if (codeCounts != null && codeCounts.Count > 0)
                {
                    // 获取计数值
                    string count = codeCounts[0].Count;

                    // 计算本次序列号
                    part5_3 = (int.Parse(codeCounts[0].Count) + 1).ToString("D3");

                    // 更新最新的序列号
                    codeTraceDao.UpdateCount(part4, orderModel.orderPlan.OrderId, part5_3);
                }
                else
                {
                    // 插入初始化数据
                    codeTraceDao.InsertCount(part4, "53011002450", part5_1, part5_3);
                }

                // 生成Part5(序列号)
                part5 = part5_1 + part5_2 + part5_3;

                return $"{part1}{part2}{part4}{part5}";
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError($"总成码生成时报错，{part1}{part2}{part4}{part5},原因是：{ex.Message}");
                return $"{part1}{part2}{part4}{part5}";
            }
        }

        /// <summary>
        /// 处理读取完成的信号，将追溯码与订单、节拍等信息绑定
        /// </summary>
        /// <param name="types">数据类型字典</param>
        /// <param name="orderModel">工单模型</param>
        private void ProcessOkStatus(Dictionary<object, Type> types, PlcOrderConfigModel orderModel)
        {
            try
            {
                List<string> operations = new List<string>();

                operations = MappingTable.AddOperation(operations, orderModel.orderPlan.ProcessCodes, orderModel.PlcName);

                // 获取工位状态配置
                Dictionary<string, bool> dictionary = saveStatus[orderModel.orderPlan.OrderId];

                if (dictionary == null && !(dictionary.Count > 0))
                    return;

                // 根据不同Plc的订单处理
                switch (orderModel.PlcName)
                {
                    case "":

                        break;
                    default:
                        dataCollect.BatchStationCollrction(types, orderModel, operations);
                        break;
                }
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError($"{orderModel.PlcName}处理读取完成的信号，将追溯码与订单、节拍等信息绑定失败，原因是：" + ex.Message);
            }
        }

        /// <summary>
        /// 根据缓存的开始时间判断是否处于可以执行推送的时间
        /// </summary>
        /// <param name="startTime"></param>
        /// <returns></returns>
        private bool CanPush(string startTime)
        {
            try
            {
                if (!string.IsNullOrEmpty(startTime))
                {
                    // 解析字符串为 DateTime 对象
                    DateTime currentDateTime = Convert.ToDateTime(startTime);//DateTime.ParseExact(startTime, "yyyy/MM/dd HH:mm:ss", null);

                    // 获取下一个整点的时间
                    DateTime nextFullHour = currentDateTime.AddHours(1).AddMinutes(-currentDateTime.Minute).AddSeconds(-currentDateTime.Second);

                    // 获取当前时间比较大小
                    DateTime now = DateTime.Now;

                    if (now < nextFullHour)
                        return true;
                }
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError("小时产量推送中是否可以执行推送判断失败，原因是：" + ex.Message);
                return true;
            }
            return false;
        }
    }
}